package com.jxlst.channel.pro.util.seq;

import org.apache.commons.lang3.concurrent.BasicThreadFactory;

import java.sql.Timestamp;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 高并发场景下System.currentTimeMillis()的性能问题的优化
 * <p><p>
 * System.currentTimeMillis()的调用比new一个普通对象要耗时的多（具体耗时高出多少我还没测试过，有人说是100倍左右）<p>
 * System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道<p>
 * 后台定时更新时钟，JVM退出时，线程自动回收<p>
 * 10亿：43410,206,210.72815533980582%<p>
 * 1亿：4699,29,162.0344827586207%<p>
 * 1000万：480,12,40.0%<p>
 * 100万：50,10,5.0%<p>
 *
 * @author lry
 */
public final class SystemClock {

    /**
     * 周期
     */
    private final long period;
    /**
     * 当前数值
     */
    private final AtomicLong now;

    private SystemClock(long period) {
        this.period = period;
        this.now = new AtomicLong(System.currentTimeMillis());
        scheduleClockUpdating();
    }

    /**
     * @return SystemClock
     */
    private static SystemClock instance() {
        return InstanceHolder.INSTANCE;
    }

    /**
     * @return long
     */
    public static long now() {
        return instance().currentTimeMillis();
    }

    /**
     * @return String
     */
    public static String nowDate() {
        return new Timestamp(instance().currentTimeMillis()).toString();
    }

    /**
     *
     */
    private void scheduleClockUpdating() {
        ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(5, new BasicThreadFactory.Builder().namingPattern("System Clock").daemon(true).build());
        scheduler.scheduleAtFixedRate(() -> now.set(System.currentTimeMillis()), period, period, TimeUnit.MILLISECONDS);
    }

    /**
     * 获取时间戳
     *
     * @return long
     */
    private long currentTimeMillis() {
        return now.get();
    }

    /**
     * 实例持久
     */
    private static class InstanceHolder {
        /**
         * 实例
         */
        public static final SystemClock INSTANCE = new SystemClock(1);
    }

}
